﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Net;
using System.Windows.Forms;

namespace GLaDOSAtHomeCommunity
{
    public class GameManager
    {
        private Thread m_Thread = null;
        private List<Game> m_GamePriority = new List<Game>();
        private string m_Running = null;
        private bool m_IsRunning = false;
        private MainForm m_Form = null;
        private delegate void EmptyDelegate();
        private delegate object ObjectDelegate();
        private string m_ServerContacting = null;

        public string GlobalTarget
        {
            get
            {
                if (this.m_GamePriority.Count == 0)
                    return null;
                else
                    return this.m_GamePriority[0].Key;
            }
        }

        public string CurrentRunning
        {
            get { return this.m_Running; }
        }

        public bool IsRunning
        {
            get { return this.m_IsRunning; }
        }

        public string ServerContacting
        {
            get { return this.m_ServerContacting; }
        }

        public GameManager(MainForm form)
        {
            this.m_Form = form;
        }

        public void StartMonitoring()
        {
            this.m_Thread = new Thread(DoMonitoring);
            this.m_Thread.IsBackground = true;
            this.m_Thread.Start();
        }

        public void StopMonitoring()
        {
            this.m_Thread.Abort();
            if (this.m_Running != null)
                this.StopGame(this.m_Running);

            this.m_Running = null;
            this.m_GamePriority.Clear();
            this.m_Thread = null;

            this.m_Form.Invoke(new EmptyDelegate(() => { this.m_Form.UpdateStatistics(); }));
        }

        private void DoMonitoring()
        {
            try
            {
                while (true)
                {
                    // Stop game if it already exists.
                    if (this.m_Running != null)
                    {
                        this.StopGame(this.m_Running);
                        this.m_Running = null;
                    }
                    this.m_IsRunning = false;

                    // Get a new game priority list.
                    bool contacted = false;
                    string res = null;
                    int ii = 0;
                    Uri toTry = Program.SourceURLs[ii];
                    while (!contacted)
                    {
                        try
                        {
                            this.m_ServerContacting = toTry.Host;
                            this.m_Form.Invoke(new EmptyDelegate(() => { this.m_Form.UpdateServerContactStatus(); }));

                            WebClient wc = new WebClient();
                            res = wc.DownloadString(toTry);
                            contacted = true;
                        }
                        catch (WebException e)
                        {
                            // Can't contact.
                            if (ii < Program.SourceURLs.Count)
                            {
                                ii += 1;
                                toTry = Program.SourceURLs[ii];
                            }
                            else
                            {
                                // Go through the list again.
                                ii = 0;
                                toTry = Program.SourceURLs[ii];
                            }
                        }
                    }
                    string[] spl = res.Split(new char[] { '\n' });
                    this.m_GamePriority.Clear();

                    // Clear the contacting information.
                    this.m_ServerContacting = null;
                    this.m_Form.Invoke(new EmptyDelegate(() => { this.m_Form.UpdateServerContactStatus(); }));

                    // Add the games to the game priority.
                    foreach (string r in spl)
                    {
                        try
                        {
                            this.m_GamePriority.Add(Program.Games[r]);
                        }
                        catch (KeyNotFoundException)
                        {
                        }
                    }

                    // Loop through the list of games, until we find one
                    // we can start.
                    for (int i = 0; i < this.m_GamePriority.Count; i++)
                    {
                        Game g = this.m_GamePriority[i];
                        if (this.HasGame(g))
                        {
                            this.m_Running = g.Key;
                            this.StartGame(this.m_Running);
                            break;
                        }
                    }

                    // Ping the statistics server.
                    this.PingStats();

                    // Check to ensure a game was started.
                    if (this.m_Running == null)
                    {
                        this.m_IsRunning = true;
                        this.m_Form.Invoke(new EmptyDelegate(() => { this.m_Form.UpdateStatistics(); }));
                        this.m_Form.Invoke(new EmptyDelegate(() => { this.m_Form.EnableStop(); }));
                    }
                    else
                    {
                        // Get the form to update the current information.
                        this.m_Form.Invoke(new EmptyDelegate(() => { this.m_Form.UpdateStatistics(); }));

                        // Run a thread to check when the process comes online.
                        new Thread(() =>
                        {
                            string proc = Program.Games[this.m_Running].ProcessName;
                            int i = 0;
                            while (Process.GetProcessesByName(proc).Length == 0 && i < 30)
                            {
                                Thread.Sleep(1000);
                                i += 1;
                                this.m_Form.Invoke(new EmptyDelegate(() => { this.m_Form.SetWaitTime(30 - i); }));
                            }

                            if (i == 10 && Process.GetProcessesByName(proc).Length == 0)
                            {
                                // Check to see whether it didn't start at all.
                                this.m_Form.Invoke(new EmptyDelegate(() => { this.m_Form.ForceStop(); }));
                            }
                            else
                            {
                                this.m_IsRunning = true;
                                this.m_Form.Invoke(new EmptyDelegate(() => { this.m_Form.UpdateStatistics(); }));
                                this.m_Form.Invoke(new EmptyDelegate(() => { this.m_Form.EnableStop(); }));
                            }
                        }).Start();
                    }

                    // Wait 10 minutes before doing it all again.
                    this.m_Form.Invoke(new EmptyDelegate(() => { this.m_Form.ResetCheckTime(); }));
                    Thread.Sleep(1000 * 60 * 10);
                }
            }
            catch (Exception e)
            {
                if (e is ThreadAbortException) { }
                else
                {
                    this.m_Form.Invoke(new EmptyDelegate(() =>
                    {
                        string msg = e.GetType().Name + "\r\n----------------\r\n" + e.Message + "\r\n" + e.StackTrace;
                        Exception ie = e.InnerException;
                        while (ie != null)
                        {
                            msg += "\r\n----------------\r\n" + ie.GetType().Name + "\r\n----------------\r\n" + ie.Message + "\r\n" + ie.StackTrace;
                            ie = ie.InnerException;
                        }
                        ErrorForm ef = new ErrorForm(msg);
                        ef.ShowDialog();
                        Application.Exit();
                    }));
                }
            }
        }

        private void StartGame(string key)
        {
            int appid = Program.Games[key].AppID;
            Process.Start("steam://run/" + appid);
        }

        private bool HasGame(Game g)
        {
            return (bool)this.m_Form.Invoke(new ObjectDelegate(() => { return this.m_Form.HasGame(g.Key); }));
        }

        private void StopGame(string key)
        {
            string proc = Program.Games[key].ProcessName;
            Process[] toterm = Process.GetProcessesByName(proc);
            foreach (Process t in toterm)
                t.Kill();
        }

        private void PingStats()
        {
            try
            {
                WebClient wc = new WebClient();
                string res = wc.DownloadString(Program.StatsURL + (this.m_Running == null ? "none" : this.m_Running));
            }
            catch (Exception) { } // We don't care if the statistics server goes down ;)
        }
    }
}
